﻿using System;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using ClassLibrary.Configuration.Switch;
using ClassLibrary.Agent;

namespace ClassLibrary
{
   public class ConnectionControler_node
    {
        public event HandleSimulationEvents ConfigReceived;

        private Socket _client;
        private readonly byte[] _data = new byte[Size];
        private const int Size = 1024 * 8;
        readonly byte[] _mDataBuffer = new byte[Size];

        public ConnectionControler_node(String ip, int port, string id)
        {
            Connect(ip, port);
            Login(id);
        }
        /// <summary>
        /// Metoda dzięki której wywoływana jest metoda 
        /// odpowiedzialna za wysłanie loginu węzła sieciowego.
        /// </summary>
        /// <param name="id">ID węzła sieciowego</param>
        private void Login(String id)
        {
            byte[] message1 = Encoding.ASCII.GetBytes(id);
            _client.BeginSend(message1, 0, message1.Length, SocketFlags.None,
                      new AsyncCallback(SendData), _client);
        }
        /// <summary>
        /// Metoda odpowiedzialna za ustanowienie połączenia 
        /// z aplikacją zarządzającą
        /// </summary>
        /// <param name="strIpAddress">Adres ip aplikacji zarządzającej</param>
        /// <param name="portNo">Port Aplikacji zarządzającej</param>
        private void Connect(String strIpAddress, int portNo)
        {
            _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //get the remote IP address...
            var ipAddress = IPAddress.Parse(strIpAddress);
            //create the end point
            var ipEndPoint = new IPEndPoint(ipAddress.Address, portNo);
            //connect to the remote host...
            _client.Connect(ipEndPoint);
            
            //watch for data ( asynchronously )...
            WaitForData();
        }
        /// <summary>
        /// Metoda odpowiedzialna za wysyłanie danych
        /// </summary>
        /// <param name="iar">Status operacji asynchronicznej</param>
        void SendData(IAsyncResult iar)
        {
            Socket remote = (Socket)iar.AsyncState;
            int sent = remote.EndSend(iar);
            remote.BeginReceive(_data, 0, Size, SocketFlags.None,
                          new AsyncCallback(OnDataReceived), remote);
        }
        /// <summary>
        /// Metoda w której nasłuchuje się w oczekiwaniu na dane
        /// </summary>
        private void WaitForData()
        {
            // now start to listen for any data...
            _client.BeginReceive(_mDataBuffer, 0, _mDataBuffer.Length, SocketFlags.None, OnDataReceived, null);
        }
        /// <summary>
        /// Metoda w której w razie nadejścia danych,
        /// są one deserializowane i wysyłane jest zdarzenie o nadejsciu danych.
        /// </summary>
        /// <param name="asyn">Status operacji asynchronicznej</param>
        private void OnDataReceived(IAsyncResult asyn)
        {
            //end receive...
            try
            {
                var bytesReceivedCount = _client.EndReceive(asyn);
                var binaryFormatter = new BinaryFormatter();
                var memoryStream = new MemoryStream(_mDataBuffer, 0, bytesReceivedCount);
                var config = (Query)binaryFormatter.Deserialize(memoryStream);

                ConfigReceived(config, EventType.ConfigReceived);
                WaitForData();
            }
                
            catch (SocketException ex)
            {
                MessageBox.Show("Manager disconected!", "Conection lost", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }
        /// <summary>
        /// Metoda w której wysyłane są dane.
        /// </summary>
        /// <param name="package"></param>
        public void Send(object package)
        {
            var binaryFormatter = new BinaryFormatter();
            var memoryStream = new MemoryStream();
            binaryFormatter.Serialize(memoryStream, package);
            var serializedData = memoryStream.ToArray();
            _client.BeginSend(serializedData, 0, serializedData.Length, SocketFlags.None, null, null);
        }

    }
}
